<?php
/*==========================================================================*/
        /*      文件名:Tree.php                                          */
        /*      功能：实现无限分类的增删改查，用于codeigniter框架，
 
/*==========================================================================*/
if (!defined('BASEPATH')) exit('No direct script access allowed');
class Tree_model extends CI_Model{
    private $CI;         //CI对象
    private $table;  //要操作的表名
    //表的8个字段
    private $id;        //分类ID
    private $pid;   //父分类ID
    private $title;   //分类名称
    private $sort;       //分类排序，在同一父级下有多级时，用于排序
    private $url;    //url
    private $level;      //分类等级,即当前目录的级别
    private $hide;    //分类显示状态
    private $group;
    private $tip;
    private $is_dev;
    //所取分类的深度
    private $depth = 0;
    private $startLevel = 0;
    /**
    * 构造函数
    * @param $arr 参数包括表名，及分类表的七个字段名，如果没有定义，则采用默认，
    * 默认值
    * 表名：menu
    * 分类ID：id        
    * 父ID：pid   
    * 分类名称：title
    * 分类排序：sort
    * 分类排序：url
    * 分类介绍：tip
    * 分类等级：level
    * 分类显示状态：hide
    */
    public function __construct($arr = array())
    {  
        //通过引用的方式赋给变量来初始化原始的CodeIgniter对象
        //$this->CI = &get_instance();
        //初始化表参数
        $this->table = (isset($arr['table'])) ? $arr['table'] : 'menu';
        $this->id = (isset($arr['id'])) ? $arr['id'] : 'id';
        $this->pid = (isset($arr['pid'])) ? $arr['pid'] : 'pid';
        $this->title = (isset($arr['title'])) ? $arr['title'] : 'title';
        $this->sort = (isset($arr['sort'])) ? $arr['sort'] : 'sort';
        $this->url = (isset($arr['url'])) ? $arr['url'] : 'url';
        $this->level = (isset($arr['level'])) ? $arr['level'] : 'level';
        $this->hide = (isset($arr['hide'])) ? $arr['hide'] : 'hide';
        $this->group = (isset($arr['group'])) ? $arr['group'] : 'group';
        $this->tip = (isset($arr['tip'])) ? $arr['tip'] : 'tip';
        $this->is_dev = (isset($arr['is_dev'])) ? $arr['is_dev'] : 'is_dev';


    }
        /**
         * 从数据库取所有分类数据，返回数组
    */
    public function fetchData($hide)
    {
        if ($hide)
        {
            $query = $this->db->from($this->table)->where(array($this->hide => 1))->order_by('sort,id asc')->get();

        }else
        {
            $query = $this->db->from($this->table)->order_by('sort,id asc')->get();
        }
        return $query->result_array();
    }
    /**
     *取某一条分类数据
     *@param $id 分类ID
    */
    public function fetchOne($id)
    {
        $this->db->where($this->id,$id);
        $query = $this->db->get($this->table);
        return $query->row_array(1);
    }
    /**
     *取出所有分类信息，返回数组，包括分类名称，一般用在select标签中显示
     * @param $pid 父类ID
         * @param $withself 查下级分类的时候，是否包含自己，默认false不包含。
     * @param $depth    所取分类的深度,值为0表示不限深度,会取所有的子分类。
     * @param $hide  分类显示状态，
    */
    public function getAllCategory($pid = 0,$withself = false,$depth = 0,$hide = false)
    {
        
        $result = array();
        $resArr = $this->fetchData($hide); //获取所有分类信息
        if($pid == 0 && $withself)
        {
            $root = array(
                $this->id => 0,
                $this->pid => -1,
                $this->title => '顶级菜单',
                $this->level => 0,
                $this->sort => 0
            );
            array_unshift($resArr, $root);  
        }
        
        if (empty($resArr))
        {
            return array();
        }
        //取得根目录
        foreach($resArr as $item)
        {
            if ($item[$this->pid] == $pid)    
            {
                $level = $item[$this->level];
            }
            if ($withself)
            {
                if ($item[$this->id] == $pid)    
                {
                    $result[] = $item;
                    $level = $item[$this->level];
                    break;
                }
             }
        }
        if (!isset($level))
        {
            return array();
        }
        $this->depth = $depth;
        $this->startLevel = $level;
        $nextLevel = $withself ? ($level + 1) : $level;
        return array_merge($result,$this->getChildren($resArr,$pid,$nextLevel));
    }
    /**
     * 取出某一分类下的所有ID，返回数组，pid = 0为根目录
     * @param $pid   父类ID
     * @param $widthself  取子分类时，是否包含自己,默认不包含
     * @param $depth      要读取的层级深度，默认查出所有子分类
    */
    public function getAllCategoryId($pid = 0,$widthself = false,$depth = 0,$hide = false)
    {
        $idArr = array();
        if ($widthself)
        {
            array_push($idArr,$pid);
        }
        $cate = $this->getAllCategory($pid,$widthself,$depth,$hide);
        foreach($cate as $item)
        {
            $idArr[] = $item[$this->id];
        }
        return $idArr;
    }
    /**
    * 用于在下拉列表框中使用
    * @param $pid 父类ID
    * @param $widthself 若取子分类的时候是否获取本身
    * @param $depth     分类深度
    * @param $hide   分类显示状态
    * @param $selectId  用于编辑分类时自动设置默认状态为selected
    */
    public function getOptionStr($pid = 0,$withself = false,$depth = 0,$hide = false,$selectId = 0)
    {
        $str = '';
        $cate = $this->getAllcategory($pid,$withself,$depth,$hide);
        if (!empty($cate))
        {
            $line = '└';
            foreach($cate as $item)
            {
                if($item[$this->pid]==-1){
                    $line = '';
                }else{
                    $line = '└';
                }
                $selected = '';
                if ($selectId != 0 && $item[$this->id] == $selectId)
                {
                    $selected = 'selected';
                }
                $str .= '<option '.$selected.' value="'.$item[$this->id].'">'.str_repeat('&nbsp;',($item[$this->level] - $this->startLevel)*2).$line.$item[$this->title].'</option>';
            }
        }
        return $str;
    }
    /**
     * 用于列表显示，按ul li标签组织
     * @param $pid   父分类ID
     * @param $widthself  若取子分类的时候是否获取本身
     * @param $widthHref  是否提供超链接，即编辑和删除链接
     * @param $depth      分类深度
     */
    public function getListStr($pid = 0,$widthself = false,$withHref = true,$depth = 0,$hide = false)
    {
        //开头
        $str = '';
        $startLevel = -1;
        $preLevel = 0;
        $cate = $this->getAllCategory($pid,$widthself,$depth,$hide);
        if (!empty($cate))
        {
            foreach($cate as $item)
            {
                if ($startLevel < 0)
                {
                    $startLevel = $item[$this->level];
                }
                if ($item[$this->level] < $preLevel) {
                    $str .='</li>'.str_repeat('</ul></li>',$preLevel - $item[$this->level]);
                }elseif ($item[$this->level] > $preLevel) {
                    $str .='<ul>';
                }        
                else
                {
                    $str .='</li>';
                }        
                if ($withHref && $item[$this->id]!= 0)
                {
                    $str .= '<li>
                        <span style="float:right;">
                            '.($this->isDisplay($item[$this->id]) ? "正常" : "待审").'
                            <a href="'.site_url('admin/menu/edit/'.$item[$this->id]).'" class="mr50 ml200">edit</a>
                            <a onclick=\'return confirm("Are your sure to delete?");\' href="'.site_url('admin/menu/delete/'.$item[$this->id]).'">del</a>
                        </span>
                        '.str_repeat(' ',($item[$this->level]-$this->startLevel)*4).'
                        <span class="fb f16">'.($this->isChildren($item[$this->id]) ? "+" : "-").'</span>
                        <input type="text" name="cname" class="span2" value="'.$item[$this->title].'" style="border:0px;" />';
                }else
                {
                    $str .= '<li>'.$item[$this->title];
                }

                $preLevel = $item[$this->level];
            }
        }
        //收尾
        $str .=str_repeat('</li></ul>',$preLevel - $startLevel + 1);
        return $str;
    }
    /**
     * 增加分类
     * @param $pid 父类ID
     * @param $title 分类名称
     * @param $tip  分类介绍
     * @param $url  分类介绍
     * @param $sort     分类排序， 只对同一级下的分类有用
     * @param $hide  分类显示状态
    */
     public function addCategory($PostData)
    {
        $title	= $PostData['title'];
        $sort	= $PostData['sort'];
        $url	= $PostData['url'];
        $pid	= $PostData['pid'];
        $group	= $PostData['group'];
        $hide	= $PostData['hide'];
        $is_dev	= $PostData['is_dev'];
        $tip	= $PostData['tip'];
        //先获取父类的类别信息
        $parentInfo = $this->fetchOne($pid);
        //获取分类的分类级别
        if (isset($parentInfo[$this->level]))
        {
            $level = $parentInfo[$this->level];      
        }else
        {
            $level = 0;
        }
        $data = array(
            $this->pid => $pid,
            $this->title => $title,
            $this->url => $url,
            $this->tip => $tip,
            $this->group => $group,
            $this->sort => $sort,
            $this->level => $level + 1,
            $this->is_dev => $is_dev,
            $this->hide => $hide
        );
        $this->db->insert($this->table,$data);
        return $this->db->affected_rows();
    }
    /**
    * 删除分类
    * @param $id 要删除的分类ID
    * @param $widthChild 是否删除下面的子分类，默认会删除
    */
    public function delCategory($id,$widthChild = true)
    {
        if ($widthChild)
        {
            $idArr = $this->getAllCategoryId($id,true);
            $this->db->where_in($this->id,$idArr);
        }else
        {
            $this->db->where($this->id,$id);
        }
        $this->db->delete($this->table);
        return $this->db->affected_rows();
    }

    /**
     * 更新分类
     * @param $id        要编辑的分类ID
     * @param $pid    父类ID
     * @param $title 分类的名称
     * @param $url  分类介绍
     * @param $sort     分类排序
     * @param $hide  分类显示状态
     */
    function editCategory($PostData)
    {
        $id     = $PostData['id'];
        $title	= $PostData['title'];
        $sort	= $PostData['sort'];
        $url	= $PostData['url'];
        $pid	= $PostData['pid'];
        $group	= $PostData['group'];
        $hide	= $PostData['hide'];
        $is_dev	= $PostData['is_dev'];
        $tip	= $PostData['tip'];

        //先获取父分类的信息
        $parentInfo = $this->fetchOne($pid);
        //获取当前等级
        if(isset($parentInfo[$this->level]))
        {
            $level = $parentInfo[$this->level];
        }else
        {
            $level = 0;
        }
        $currentInfo = $this->fetchOne($id);
        $newLevel = $level + 1;
        $levelDiff = $newLevel - $currentInfo[$this->level];
        //修改子分类的level
        if(0 != $levelDiff)
        {
            $childIdArr = $this->getAllCategoryId($id);
            foreach($childIdArr as $item)
            {
                $this->db->set($this->level, $this->level.'+'.$levelDiff, FALSE);
                $this->db->where($this->id, $item);
                $this->db->update($this->table);
            }
        }
        //修改自己的信息
        $data = array(
            $this->pid => $pid,
            $this->title => $title,
            $this->url => $url,
            $this->tip => $tip,
            $this->group => $group,
            $this->sort => $sort,
            $this->level => $newLevel,
            $this->is_dev => $is_dev,
            $this->hide => $hide

        );                
        $this->db->where($this->id, $id);
        $this->db->update($this->table, $data);
        return $this->db->affected_rows();
    }  
    
    /**
    * 按顺序返回分类数组,用递归实现
    * @param unknown_type $cateArr
    * @param unknown_type $pid
    * @param unknown_type $level
   */
    private function getChildren($cateArr,$pid=0,$level = 1)
    {
        if($this->depth != 0 && ($level >=($this->depth + $this->startLevel)))
        {
            return array();
        }
        $resultArr = array();
        $childArr = array();

        //遍历当前父ID下的所有子分类
        foreach($cateArr as $item)
        {
            if($item[$this->pid] == $pid && ($item[$this->level] == $level))
            {
                //将子分类加入数组
                $childArr[] = $item;
            }
        }
        if(count($childArr) == 0)
        {
            //不存在下一级，无需继续
            return array();
        }

        //存在下一级，按sort排序先
        //usort($childArr,'comparebysort');   
        foreach($childArr as $item)
        {
            $resultArr[] = $item;
            $temp = $this->getChildren($cateArr,$item[$this->id],($item[$this->level] + 1));    
            if(!empty($temp))
            {
                $resultArr = array_merge($resultArr, $temp);
            }                                
        }
        return $resultArr;
    }
    //比较函数,提供usort函数用
    function comparebysort($a, $b)
    {
        if ($a == $b)
        {
            return 0;
        }
        return ($a[$this->sort] > $b[$this->sort]) ? 1 : -1;        
    }
    

    //判断是否有子类别
    function isChildren($id)
    {
        //从数据库中取出只有pid字段的数据，返回数组
        $this->db->select($this->pid);
        $query = $this->db->get($this->table);
        $resArr = $query->result_array();
        foreach ($resArr as $v)
        {
            $arr[] = $v[$this->pid];
        }
        return (in_array($id,array_unique($arr))) ? true : false;
    }

    //判断状态是否启用
    function isDisplay($id)
    {
        $query = $this->fetchOne($id);
        return ($query[$this->hide] == 1) ? true : false;
    }
}